"""Extensible JSON <http://json.org> encoder for Python data structures.

       Supports the following objects and types by default:

       +-------------------+---------------+
       | Python            | JSON          |
       +===================+===============+
       | dict              | object        |
       +-------------------+---------------+
       | list, tuple       | array         |
       +-------------------+---------------+
       | str               | string        |
       +-------------------+---------------+
       | int, float        | number        |
       +-------------------+---------------+
       | True              | true          |
       +-------------------+---------------+
       | False             | false         |
       +-------------------+---------------+
       | None              | null          |
       +-------------------+---------------+

       To extend this to recognize other objects, subclass and implement a
       ``.default()`` method with another method that returns a serializable
       object for ``o`` if possible, otherwise it should call the superclass
       implementation (to raise ``TypeError``).
   """

import dataclasses
import datetime
import json
from typing import Any


class EnhancedJSONEncoder(json.JSONEncoder):
    """convert dataclass to json dict"""

    def default(self, o: Any) -> Any:
        if dataclasses.is_dataclass(o):
            return o.to_json_dict()
        elif isinstance(o, datetime.datetime):
            r = o.isoformat()
            if o.microsecond:
                r = r[:23] + r[26:]
            if r.endswith('+00:00'):
                r = r[:-6] + 'Z'
            return r
        elif isinstance(o, datetime.date):
            return o.isoformat()
        else:
            return super().default(o)


def dict_to_json_str(o: Any) -> str:
    """convert python object into a json string"""
    return json.dumps(o, cls=EnhancedJSONEncoder, sort_keys=True)


def json_str_to_dict(s: str) -> Any:
    """convert json string into python object"""
    return json.loads(s)


if __name__ == '__main__':
    a = """{"key1": "value1", "key2": "value2"}"""
    json_str_to_dict(a)
